package com.cryptape.cita_wallet.service.http;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.alm.token.MyApp;
import com.cryptape.cita_wallet.bean.ChibiBean;
import com.cryptape.cita_wallet.bean.HttpResponse;
import com.cryptape.cita_wallet.bean.MangoAssociationBean;
import com.cryptape.cita_wallet.bean.MangoNodeBean;
import com.cryptape.cita_wallet.bean.MiningOrderIncomeBean;
import com.cryptape.cita_wallet.bean.MiningWithdrawDetailBean;
import com.cryptape.cita_wallet.bean.MyHMIOOrderBean;
import com.cryptape.cita_wallet.bean.VersionUpdateBean;
import com.cryptape.cita_wallet.constant.ConstantUtil;
import com.cryptape.cita_wallet.constant.url.HttpUrls;
import com.cryptape.cita_wallet.exception.BizException;
import com.cryptape.cita_wallet.item.CurrencyId;
import com.cryptape.cita_wallet.item.CurrencyIdList;
import com.cryptape.cita_wallet.item.Wallet;
import com.cryptape.cita_wallet.item.response.CollectionResponse;
import com.cryptape.cita_wallet.service.ContractService;
import com.cryptape.cita_wallet.util.LogUtil;
import com.cryptape.cita_wallet.util.db.DBWalletUtil;
import com.cryptape.cita_wallet.util.ether.EtherUtil;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;
import org.web3j.abi.datatypes.Type;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by BaojunCZ on 2018/8/3.
 */
public class TokenService {

    private static final String PARAM_ID = "@ID";
    private static final String PARAM_CURRENCY = "@Currency";

    private static ArrayList<CurrencyId> list = null;

    public static Observable<String> getCurrency(String symbol, String currency) {
        if (symbol.equals("GLD") && (EtherUtil.getEthNodeName().equals(ConstantUtil.ETH_MAIN_NAME))) {
            symbol = "MGP";
        }
        if (symbol.equals("MGP")) {
            return Observable.fromCallable(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    String url = "https://api.bkex.co/v1/q/ticker/price?pair=MGP_USDT";
                    Request request = new Request.Builder().url(url).build();
                    Call call = HttpService.getHttpClient().newCall(request);
                    try {
                        String response = call.execute().body().string();
                        return fetchMGPPriceFromResponse(response, currency);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            }).subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread());
        }

        return getTokenID(symbol)
                .subscribeOn(Schedulers.newThread())
                .flatMap(new Func1<String, Observable<String>>() {
                    @Override
                    public Observable<String> call(String ID) {
                        if (!TextUtils.isEmpty(ID)) {
                            String url = HttpUrls.TOKEN_CURRENCY.replace(PARAM_ID, ID).replace(PARAM_CURRENCY, currency);
                            Request request = new Request.Builder().url(url).build();
                            Call call = HttpService.getHttpClient().newCall(request);
                            try {
                                String response = call.execute().body().string();
                                return Observable.just(fetchPriceFromResponse(response, currency));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        return Observable.just(null);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<VersionUpdateBean> getVersion() {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.VERSION_URL;

            RequestBody requestBody = new FormBody.Builder()
                    .add("appType", "1")
                    .build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, VersionUpdateBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<String> getTokenCurrency(String ID, String currency) {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.TOKEN_CURRENCY.replace("@ID", ID).replace("@Currency", currency);
            Request request = new Request.Builder().url(url).build();
            Call call = HttpService.getHttpClient().newCall(request);
            return fetchPriceFromResponse(call.execute().body().string(), currency);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    private static String fetchMGPPriceFromResponse(String response, String currency) throws JSONException {
        JSONObject object = new JSONObject(response);
        String code = object.optString("code");
        if (!code.equals("0")) {
            return "0";
        }
        JSONObject data = object.optJSONObject("data");
        if (data != null) {
            return data.optString("price");
        } else {
            return null;
        }
    }

    private static String fetchPriceFromResponse(String response, String currency) throws JSONException {
        JSONObject object = new JSONObject(response);
        JSONObject data = object.optJSONObject("data");
        JSONObject quotes = data.optJSONObject("quotes");
        JSONObject quote = quotes.optJSONObject(currency);
        if (quote != null) {
            return quote.optString("price");
        } else {
            return null;
        }
    }

    public static Observable<String> getTokenID(String symbol) {
        return Observable.fromCallable(() -> {
            if (list == null) {
                Request request = new Request.Builder().url(HttpUrls.TOKEN_ID).build();
                Call call = HttpService.getHttpClient().newCall(request);
                String response = "";
                try {
                    response = call.execute().body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                list = new Gson().fromJson(response, CurrencyIdList.class).getList();
            }
            return list;
        }).subscribeOn(Schedulers.newThread())
                .flatMap((Func1<ArrayList<CurrencyId>, Observable<String>>) currencyIDItems -> {
                    for (CurrencyId item : list) {
                        if (item.getSymbol().equals(symbol)) {
                            return Observable.just(item.getId());
                        }
                    }
                    return Observable.just(null);
                });
    }


    /**
     * get ERC721 token transaction list
     *
     * @param context
     * @return
     */
    public static Observable<CollectionResponse> getCollectionList(Context context) {
        Wallet wallet = DBWalletUtil.getCurrentWallet(context);
        return Observable.fromCallable(new Callable<CollectionResponse>() {
            @Override
            public CollectionResponse call() throws Exception {
                Request request = new Request.Builder()
                        .url(HttpUrls.COLLECTION_LIST_URL + wallet.address).build();
                Call call = HttpService.getHttpClient().newCall(request);
                String response = "";
                try {
                    response = call.execute().body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return new Gson().fromJson(response, CollectionResponse.class);
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }


    public static Observable<MangoNodeBean> getTokgetMangoNode() {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getMangonodeUrl();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .add("mid", "").build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, MangoNodeBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<MyHMIOOrderBean> getMyHMIOOrder() {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getMyHMIOOrders();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, MyHMIOOrderBean.class);
        }).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<MiningOrderIncomeBean> getMiningOrderIncome(String page) {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getMiningOrderIncome();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .add("page", page)
                    .add("limit", "50")
                    .build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            Log.i("getMiningOrderIncome==","data = "+data);
            return new Gson().fromJson(data, MiningOrderIncomeBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<MiningWithdrawDetailBean> getWithdrawIndex() {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getMiningOrderIncomeIndex();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, MiningWithdrawDetailBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<HttpResponse> withdrawMiningIncome(String money) {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getWithdrawMinerIncome();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
//                    .add("money", money)
                    .build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, HttpResponse.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<ChibiBean> getChiBI() {
        return Observable.fromCallable(() -> {
            String url = HttpUrls.getChibiUrl();
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .add("mid", "").build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, ChibiBean.class);
        }).subscribeOn(Schedulers.newThread());
//                .observeOn(AndroidSchedulers.mainThread());
    }


    public static Observable<MangoAssociationBean> getTokgetMangoAssociationenCurrency(String mid) {
        return Observable.fromCallable(() -> {
            String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
            String url = HttpUrls.getMangoassociationUrl();
            RequestBody requestBody = new FormBody.Builder()
                    .add("address", address)
                    .add("mid", mid).build();
            Request request = new Request.Builder().url(url).post(requestBody).build();
            Call call = HttpService.getHttpClient().newCall(request);
            String data = call.execute().body().string();
            return new Gson().fromJson(data, MangoAssociationBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<Boolean> checkMidExist(String midCode) {
        return Observable.fromCallable(() -> {
            return ContractService.checkMemberExistsMid(midCode);
//            return new Gson().fromJson(data,MangoAssociationBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<byte[]> registerBytes(String midCode) {
        return Observable.fromCallable(() -> {
            return ContractService.registerBytes(midCode);
//            return new Gson().fromJson(data,MangoAssociationBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<byte[]> buyBytes(int type) {
        return Observable.fromCallable(() -> {
            return ContractService.buyBytes(type);
//            return new Gson().fromJson(data,MangoAssociationBean.class);
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }


    public static Observable<List<Type>> minerDetail() {
        return Observable.create(new Observable.OnSubscribe<List<Type>>() {
            @Override
            public void call(Subscriber<? super List<Type>> subscriber) {
                try {
                    List<Type> datas = ContractService.minerDetail();
                    subscriber.onNext(datas);
                } catch (BizException e) {
                    e.printStackTrace();
                    subscriber.onError(e);
                }
            }
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());

//        return Observable.fromCallable(() -> {
//            try {
//                return ContractService.minerDetail();
//            } catch (BizException e) {
//                e.printStackTrace();
//            }
////            return new Gson().fromJson(data,MangoAssociationBean.class);
//        }).subscribeOn(Schedulers.newThread())
//                .observeOn(AndroidSchedulers.mainThread());
    }

//    public static Observable<List<Type>> minerDetail() {
//        return Observable.fromCallable(() -> {
//            try {
//                return ContractService.minerDetail();
//            } catch (BizException e) {
//                e.printStackTrace();
//            }
////            return new Gson().fromJson(data,MangoAssociationBean.class);
//        }).subscribeOn(Schedulers.newThread())
//                .observeOn(AndroidSchedulers.mainThread());
//    }


    public static Observable<Map<String, String>> getExchangeInfo() {
        return Observable.fromCallable(() -> {
            return ContractService.roundDetail();
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<Boolean> checkMemberExistsAddress() {
        return Observable.fromCallable(() -> {
            try {
                Log.e("dym", "MidQueryService  checkMemberExistsAddress thread:" + Thread.currentThread());
                return ContractService.checkMemberExistsAddress();
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        })
                .subscribeOn(Schedulers.newThread());
//                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<List<Type>> queryMember() {
        return Observable.fromCallable(() -> {
            try {
                return ContractService.queryMember();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }).subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<String> getMangoAssociation(String symbol) {
        return Observable.fromCallable(() -> {
            if (list == null) {
                Request request = new Request.Builder().url(HttpUrls.TOKEN_ID).build();
                Call call = HttpService.getHttpClient().newCall(request);
                String response = "";
                try {
                    response = call.execute().body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                list = new Gson().fromJson(response, CurrencyIdList.class).getList();
            }
            return list;
        }).subscribeOn(Schedulers.newThread())
                .flatMap((Func1<ArrayList<CurrencyId>, Observable<String>>) currencyIDItems -> {
                    for (CurrencyId item : list) {
                        if (item.getSymbol().equals(symbol)) {
                            return Observable.just(item.getId());
                        }
                    }
                    return Observable.just(null);
                });
    }


}
